/**
******************************************************************************************
* @file SoundEngineer.c
* @brief Management of the sound intensity bricklet.
* @date 9 avril 2015
* @author Benjamin Lancinot
*****************************************************************************************/
/* COPYRIGHT ****************************************************************************
*****************************************************************************************
* Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************************/

#include "SoundEngineer.h"

/*******************************************************************************************
* PROTOTYPES
********************************************************************************************/

float giveBBState(float,  uint16_t);

float filterData(Protected_data,  uint16_t);

void cb_intensity(uint16_t, void *);

void SoundEngineer_initThreshold(SoundEngineer *);

void SoundEngineer_destroyUtilities(SoundEngineer*);

void SoundEngineer_destroy(SoundEngineer**);

int SoundEngineer_registerObserver(SoundEngineer*, Observer*);

int SoundEngineer_unregisterObserver(SoundEngineer*, Observer*);

/*******************************************************************************************
* FUNCTIONS
********************************************************************************************/

/**
 * @brief Constructor
 * @param ipcon Ipconnection for link bricklet to the rest of bricks/bricklets
 * @return Pointer to a new SoundEngineer
 **/
SoundEngineer* SoundEngineer_create(IPConnection ipcon){
	SoundEngineer* This = (SoundEngineer *) malloc(sizeof(*This));

	//Creation of sound intensity sensor
	SoundIntensity sound_sensor;
	sound_intensity_create(&sound_sensor, "mk8", &ipcon);
	This->soundSensor = sound_sensor;

        //Init functions
	This->initThreshold=SoundEngineer_initThreshold;
        This->destroy = SoundEngineer_destroy;
	This->subject = subjectNew(This, 3);
	This->registerObserver = SoundEngineer_registerObserver;
	This->unregisterObserver = SoundEngineer_unregisterObserver;

        //Init variable
        This->BBState = 0;
        This->noiseThreshold.value[0] = 250;

        //Init mutex
	if (pthread_mutex_init(&This->noiseThreshold.mutex, NULL) != 0)
	{
		printf("\n mutex init failed\n");
	}

	return This;
}


/**
 * @brief Using for determining BBState (1= quiet or 2= noisy) according to the noisy threshold
 * @param noiseThreshold Value of the threshold
 * @param  intensity Value of the sound intensity given by the bricklet
 * @return The good value of BBState
 **/
float giveBBState(float noiseThreshold,  uint16_t intensity){
	//the intensity look like quiet
	if (intensity <= noiseThreshold){
		return 1;
        }
        //the intensity look like noisy
	else {
		return 2;
	}
}

/**
 * @brief Examine 4 data of sound and determine which state is the most suitable for describe the sound on the room
 * @param noiseThreshold Value of the threshold
 * @param  intensity Value of the sound intensity given by the bricklet
 * @return The good value of BBState
 **/
float filterData(Protected_data noiseThreshold,  uint16_t intensity){
        static int counter=0;
        static float filteredData=1;
        float newBBState;

        //Recover the new BBState
        pthread_mutex_lock(&noiseThreshold.mutex);
        newBBState = giveBBState(noiseThreshold.value[0], intensity);
        pthread_mutex_unlock(&noiseThreshold.mutex);

        if(counter>=4){
                counter =0;
		newBBState = filteredData;
		filteredData = 1;
		return newBBState;
        }else{
              if (newBBState == 2 || filteredData == 2){
                  filteredData = 2;
              }
              counter++;
              return 0;
        }

}

/**
 * @brief Use by the callback function for notify observers and recover the good BBState
 * @param intensity Value of the sound intensity given by the bricklet
 * @param user_data Pointer to the wanted SoundEngineer for recover thresholds
 **/
void cb_intensity(uint16_t intensity, void *user_data) {
	float newBBState;
	SoundEngineer* This;
	This = (SoundEngineer*) user_data;

        //Recover the new BBState
        newBBState = filterData(This->noiseThreshold, intensity);

        //notify observers when the BBState changed
	if ( This->BBState != newBBState && newBBState !=0){
		This->BBState = newBBState;
		This->subject->notifyObservers(This->subject);
        }
}


/**
 * @brief Init the SoundEngineer and callback function
 * @param This Pointer to the wanted SoundEngineer
 **/
void SoundEngineer_initThreshold(SoundEngineer *This){
        // Create the callback. It's called every second
	sound_intensity_set_intensity_callback_period(&This->soundSensor, 1000);

	// Register intensity callback to function cb_intensity
	sound_intensity_register_callback(&This->soundSensor,
	                                  SOUND_INTENSITY_CALLBACK_INTENSITY,
	                                  (void *)cb_intensity,
	                                  (void *) This);

       //Recover the first baby state
        uint16_t intensity;
        sound_intensity_get_intensity(&This->soundSensor, &intensity);

        pthread_mutex_lock(&This->noiseThreshold.mutex);
        This->BBState = giveBBState(This->noiseThreshold.value[0], intensity);
        pthread_mutex_unlock(&This->noiseThreshold.mutex);
}


/**
 * @brief Destroy all of the components of SoundEngineer
 * @param This Pointer to the wanted SoundEngineer
 **/
void SoundEngineer_destroyUtilities(SoundEngineer* This)
{
	This->subject->destroy(This->subject);
        pthread_mutex_destroy(&This->noiseThreshold.mutex);
        sound_intensity_destroy(&This->soundSensor);
}

/**
 * @brief Destructor
 * @param This Double pointer to the wanted SoundEngineer
 **/
void SoundEngineer_destroy(SoundEngineer** This)
{
        SoundEngineer_destroyUtilities(* This);
	if (*This) {
		free(*This);
		*This = NULL;
	}
}

/**
 * @brief Proxy method for Register an observer for the SoundEngineer object.
 * @param This Pointer to the wanted SoundEngineer
 * @param This Pointer to the wanted observer
 * @return The result of the development of the registration (0 = GOOD and 1 = NOT GOOD)
 **/
int SoundEngineer_registerObserver(SoundEngineer* This, Observer* observer) {
	return This->subject->registerObserver(This->subject, observer);
}

/**
 * @brief Proxy method for Unregister observer for the SoundEngineer object.
 * @param This Pointer to the wanted SoundEngineer
 * @param This Pointer to the wanted observer
 * @return The result of the development of the unregistration (0 = GOOD and 1 = NOT GOOD)
 **/
int SoundEngineer_unregisterObserver(SoundEngineer* This, Observer* observer) {
	return This->subject->unregisterObserver(This->subject, observer);
}
